/* Copyright (C) 2013-2019 by Jacob Alexander
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#pragma once

// ----- Includes -----

// Compiler Includes
#include <stdint.h>

// Local Includes
#include <buildvars.h> // Defines USB Parameters, partially generated by CMake



// ----- Defines -----

// Max size of key buffer needed for NKRO
// Boot mode uses only the first 6 bytes
#define USB_NKRO_BITFIELD_SIZE_KEYS 28
#define USB_BOOT_MAX_KEYS 6



// ----- Enumerations -----

// USB NKRO state transitions (indicates which Report ID's need refreshing)
// Boot mode just checks if any keys were changed (as everything is sent every time)
typedef enum USBKeyChangeState {
	USBKeyChangeState_None          = 0x00,
	USBKeyChangeState_Modifiers     = 0x01,
	USBKeyChangeState_Keys          = 0x02,
	USBKeyChangeState_System        = 0x04,
	USBKeyChangeState_Consumer      = 0x08,
	USBKeyChangeState_All           = 0x0F,
} USBKeyChangeState;

// Allows for selective USB descriptor pushes
// However, in most cases everything is updated for each packet push
typedef enum USBMouseChangeState {
	USBMouseChangeState_None      = 0x00,
	USBMouseChangeState_Buttons   = 0x01,
	USBMouseChangeState_Relative  = 0x02,
	USBMouseChangeState_WheelVert = 0x04,
	USBMouseChangeState_WheelHori = 0x08,
	USBMouseChangeState_All       = 0xFF,
} USBMouseChangeState;



// ----- Structs -----

// Buffer structure for USB HID keyboard output
typedef struct USBKeys {
	// Which modifier keys are currently pressed
	// 1=left ctrl,    2=left shift,   4=left alt,    8=left gui
	// 16=right ctrl, 32=right shift, 64=right alt, 128=right gui
	uint8_t  modifiers;

	// Currently pressed keys, max is defined by USB_MAX_KEY_SEND
	uint8_t  keys[USB_NKRO_BITFIELD_SIZE_KEYS];

	// System Control and Consumer Control 1KRO containers
	uint8_t  sys_ctrl;
	uint16_t cons_ctrl;

	// Indicate if USB should send update
	// OS only needs update if there has been a change in state
	USBKeyChangeState changed;
} USBKeys;

// Buffer structure for USB HID mouse output
typedef struct USBMouse {
	// Currently pressed mouse buttons, bitmask, 0 represents no buttons pressed
	uint16_t buttons;

	// Relative mouse axis movement, stores pending movement
	int16_t  relative_x;
	int16_t  relative_y;

	// Mouse wheel pending action
	int8_t   vertwheel;
	int8_t   horiwheel;

	// Indicate if USB should send update
	// OS only needs update if there has been a change in state
	USBMouseChangeState changed;
} USBMouse;



// ----- Variables -----

// Variables used to communciate to the output module
// XXX Even if the output module is not USB, this is internally understood keymapping scheme
extern volatile USBKeys  USBKeys_primary;
extern volatile USBKeys  USBKeys_idle;

extern volatile uint8_t  USBKeys_Sent;
extern volatile uint8_t  USBKeys_LEDs;
extern volatile uint8_t  USBKeys_LEDs_prev;

extern volatile uint8_t  USBKeys_Protocol; // 0 - Boot Mode, 1 - NKRO Mode
extern volatile uint8_t  USBKeys_Protocol_New;
extern volatile uint8_t  USBKeys_Protocol_Change;

extern volatile USBMouse USBMouse_primary;

// Keeps track of the idle timeout refresh (used on Mac OSX)
extern volatile uint8_t  USBKeys_Idle_Config;
extern volatile uint32_t USBKeys_Idle_Expiry;
extern volatile uint8_t  USBKeys_Idle_Count; // AVR only

extern volatile uint8_t  Output_Available; // 0 - Output module not fully functional, 1 - Output module working

extern          uint8_t  Output_DebugMode; // 0 - Debug disabled, 1 - Debug enabled, 2 - Extra debug

extern          uint16_t Output_ExtCurrent_Available; // mA - Set by outside module if not using USB (i.e. Interconnect)

extern volatile uint32_t USBInit_TimeStart; // Timetamp when usb_init was triggered
extern volatile uint32_t USBInit_TimeEnd;   // Timetamp since last call to the Configuration endpoint
extern volatile uint16_t USBInit_Ticks;     // Number of times the end time has been updated

extern volatile uint8_t  USBDev_Address;    // USB Address - Set by host, unique to the bus

extern volatile uint32_t USBStatus_FrameErrors; // Counter of SOF (start of frame) errors



// ----- Functions -----

void USB_setup();
void USB_poll();
void USB_periodic();

void USB_flushBuffers();

void USB_firmwareReload(); // Request firmware reload
void USB_softReset();      // Request soft reset

unsigned int USB_availablechar();

int USB_getchar();
int USB_putchar( char c );
int USB_putstr( char* str );

unsigned int USB_rawio_availablechar();
int USB_rawio_getbuffer( char* buffer );
int USB_rawio_sendbuffer( char* buffer );

void USB_ConsCtrlDebug( USBKeys *buffer );
void USB_SysCtrlDebug( USBKeys *buffer );
void USB_6KRODebug( USBKeys *buffer );
void USB_NKRODebug( USBKeys *buffer );

